import io
import json
import os

import urllib3
from minio import Minio

from configs.global_config import global_config
from utils.logger_config import get_logger_config


class MinioConfig:
    """MinIO配置类"""

    def __init__(
        self,
        endpoint=None,
        access_key=None,
        secret_key=None,
        secure=None,
        bucket=None,
        public_url=None,
    ):
        """
        初始化MinIO配置

        Args:
            endpoint: MinIO服务端点，格式为"host:port"
            access_key: 访问密钥
            secret_key: 秘密密钥
            secure: 是否使用HTTPS
            bucket: 默认存储桶
            public_url: 公共访问URL前缀
        """
        # 从参数或环境变量获取配置
        self.endpoint = endpoint or os.environ.get("MINIO_ENDPOINT", "localhost:9000")
        self.access_key = access_key or os.environ.get("MINIO_ACCESS_KEY", "minioadmin")
        self.secret_key = secret_key or os.environ.get("MINIO_SECRET_KEY", "minioadmin")

        # 处理布尔值
        if secure is None:
            secure_str = os.environ.get("MINIO_SECURE", "false")
            self.secure = secure_str.lower() == "true"
        else:
            self.secure = secure

        self.bucket = bucket or os.environ.get("MINIO_BUCKET", "rongmei-video-frames")

        # 如果没有提供公共URL，则根据endpoint和secure构造
        if not public_url:
            protocol = "https" if self.secure else "http"
            self.public_url = os.environ.get(
                "MINIO_PUBLIC_URL", f"{protocol}://{self.endpoint}"
            )
        else:
            self.public_url = public_url

    def to_dict(self):
        """将配置转换为字典"""
        return {
            "endpoint": self.endpoint,
            "access_key": self.access_key,
            "secret_key": self.secret_key,
            "secure": self.secure,
            "bucket": self.bucket,
            "public_url": self.public_url,
        }


class MinioClient:
    """MinIO客户端封装类"""

    def __init__(self, config=None):
        """
        初始化MinIO客户端

        Args:
            config: MinioConfig实例，如果为None则创建默认配置
        """
        self.config = config if config else MinioConfig()
        self.client = None
        self.logger = get_logger_config(name="minio_client").get_logger()
        # 初始化客户端
        self.init_client()
        global_config.initialize()
        print(global_config.raw_config)

    def init_client(self):
        """初始化MinIO客户端"""
        try:
            # 配置超时和重试策略
            http_client = urllib3.PoolManager(
                timeout=urllib3.util.Timeout(
                    connect=global_config.get("minio.connect_timeout", 10),  # 连接超时时间
                    read=global_config.get("minio.read_timeout", 10),  # 读取超时时间
                ),
                retries=urllib3.util.Retry(
                    total=global_config.get("minio.retries", 3),  # 总重试次数
                    backoff_factor=0.3,  # 指数退避因子
                ),
            )

            # 创建MinIO客户端，添加超时和重试参数
            self.client = Minio(
                self.config.endpoint,  # 确保使用正确的端点
                access_key=self.config.access_key,  # 确保使用正确的访问密钥
                secret_key=self.config.secret_key,  # 确保使用正确的密钥
                secure=self.config.secure,  # 是否使用HTTPS
                http_client=http_client,  # 使用自定义的HTTP客户端，包括超时和重试
            )

            # 确保存储桶存在
            self.ensure_bucket_exists()

            self.logger.info(f"MinIO客户端初始化成功，存储桶: {self.config.bucket}")
            return True
        except Exception as e:
            self.logger.error(f"MinIO客户端初始化失败: {str(e)}")
            self.client = None
            return False

    def ensure_bucket_exists(self):
        """确保存储桶存在，如果不存在则创建"""
        if not self.client:
            return False

        try:
            # 设置超时时间，避免长时间等待
            import socket

            socket.setdefaulttimeout(3)  # 设置3秒超时

            if not self.client.bucket_exists(self.config.bucket):
                self.client.make_bucket(self.config.bucket)
                # 设置存储桶为公共可读
                policy = {
                    "Version": "2012-10-17",
                    "Statement": [
                        {
                            "Effect": "Allow",
                            "Principal": {"AWS": "*"},
                            "Action": ["s3:GetObject"],
                            "Resource": [f"arn:aws:s3:::{self.config.bucket}/*"],
                        }
                    ],
                }
                self.client.set_bucket_policy(self.config.bucket, json.dumps(policy))
                self.logger.info(f"创建存储桶并设置为公共可读: {self.config.bucket}")
            return True
        except Exception as e:
            self.logger.error(f"确保存储桶存在时出错: {str(e)}")
            return False
        finally:
            # 恢复默认超时设置
            socket.setdefaulttimeout(None)

    def is_available(self):
        """检查MinIO客户端是否可用"""
        return self.client is not None

    def upload_file(self, file_path, object_name=None, bucket_name=None):
        """
        上传文件到MinIO

        Args:
            file_path: 本地文件路径
            object_name: 对象名称，如果为None则使用文件名
            bucket_name: 存储桶名称，如果为None则使用默认存储桶

        Returns:
            成功返回对象URL，失败返回None
        """
        if not self.client:
            self.logger.error("MinIO客户端未初始化")
            return None

        try:
            bucket = bucket_name or self.config.bucket

            # 如果没有提供对象名称，则使用文件名
            if not object_name:
                object_name = os.path.basename(file_path)

            # 上传文件
            self.client.fput_object(
                bucket_name=bucket,
                object_name=object_name,
                file_path=file_path,
                content_type=self._get_content_type(file_path),
            )

            # 返回对象URL
            return f"{self.config.public_url}/{bucket}/{object_name}"
        except Exception as e:
            self.logger.error(f"上传文件失败: {str(e)}")
            return None

    def upload_bytes(self, data, object_name, content_type=None, bucket_name=None):
        """
        上传字节数据到MinIO

        Args:
            data: 字节数据或BytesIO对象
            object_name: 对象名称
            content_type: 内容类型，如果为None则自动检测
            bucket_name: 存储桶名称，如果为None则使用默认存储桶

        Returns:
            成功返回对象URL，失败返回None
        """
        if not self.client:
            self.logger.error("MinIO客户端未初始化")
            return None

        try:
            bucket = bucket_name or self.config.bucket

            # 如果data是字节而不是BytesIO，则转换
            if isinstance(data, bytes):
                data = io.BytesIO(data)

            # 如果没有提供内容类型，则根据对象名称猜测
            if not content_type:
                content_type = self._get_content_type(object_name)

            # 获取数据长度
            data.seek(0, os.SEEK_END)
            length = data.tell()
            data.seek(0)

            # 上传数据
            self.client.put_object(
                bucket_name=bucket,
                object_name=object_name,
                data=data,
                length=length,
                content_type=content_type,
            )

            # 返回对象URL
            return f"{self.config.public_url}/{bucket}/{object_name}"
        except Exception as e:
            self.logger.error(f"上传数据失败: {str(e)}")
            return None

    def delete_object(self, object_name, bucket_name=None):
        """
        删除MinIO中的对象

        Args:
            object_name: 对象名称
            bucket_name: 存储桶名称，如果为None则使用默认存储桶

        Returns:
            成功返回True，失败返回False
        """
        if not self.client:
            self.logger.error("MinIO客户端未初始化")
            return False

        try:
            bucket = bucket_name or self.config.bucket
            self.client.remove_object(bucket, object_name)
            return True
        except Exception as e:
            self.logger.error(f"删除对象失败: {str(e)}")
            return False

    def _get_content_type(self, file_path):
        """根据文件扩展名获取内容类型"""
        ext = os.path.splitext(file_path)[1].lower()
        content_types = {
            ".jpg": "image/jpeg",
            ".jpeg": "image/jpeg",
            ".png": "image/png",
            ".gif": "image/gif",
            ".bmp": "image/bmp",
            ".mp4": "video/mp4",
            ".avi": "video/x-msvideo",
            ".mov": "video/quicktime",
            ".mp3": "audio/mpeg",
            ".wav": "audio/wav",
            ".pdf": "application/pdf",
            ".txt": "text/plain",
            ".html": "text/html",
            ".json": "application/json",
            ".xml": "application/xml",
            ".zip": "application/zip",
        }
        return content_types.get(ext, "application/octet-stream")
